package com.ybear.ybcomponent.widget.linkage

import android.os.SystemClock
import android.util.SparseArray
import android.view.MotionEvent
import android.view.View.OnTouchListener
import android.view.ViewGroup
import androidx.core.util.containsKey
import androidx.core.widget.NestedScrollView.OVER_SCROLL_NEVER
import com.ybear.ybcomponent.widget.linkage.effector.AlphaEffector
import com.ybear.ybcomponent.widget.linkage.effector.ExtendEffector
import com.ybear.ybcomponent.widget.linkage.effector.ExtendRecoveryEffector
import com.ybear.ybcomponent.widget.linkage.effector.ZoomEffector
import com.ybear.ybcomponent.widget.linkage.effector.base.CoreEffector
import com.ybear.ybcomponent.widget.linkage.enums.ViewLinkage
import com.ybear.ybcomponent.widget.linkage.interfaces.ILinkage
import com.ybear.ybcomponent.widget.linkage.interfaces.OnScrollListener
import kotlin.math.abs

/**
 * @Author MiWi
 * @Date 2025年2月13日
 * @Description Linkage - 帮助类
 */
open class LinkageAnimationHelper : ILinkage {
    private val motionEventPool = ArrayDeque<MotionEvent>()
    private val effectorArray = SparseArray<CoreEffector>()
    private val touchListeners = mutableListOf<OnTouchListener>()
    private val scrollListener = mutableListOf<OnScrollListener>()
    private var currentAction: Int = MotionEvent.ACTION_UP
    private var isInterceptTouch: Boolean = false
    private var touchLastX = 0F
    private var touchLastY = 0F
    private var touchOldX = 0F
    private var touchOldY = 0F
    private var touchScrollX = 0F
    private var touchScrollY = 0F

    /**
     * 当布局配置文件中没有定义 app:linkageScroll="zoom|alpha|extend" 等其中一个效果器时，
     * 可以通过 [LinkageAnimationHelper.coreEffector] 设置效果器。
     *
     * #CUSTOM
     * 当定义 app:linkageScroll="custom" 时，必须要设置 [LinkageAnimationHelper.coreEffector]，
     * 并且自定义的效果器中 [CoreEffector.onViewLinkage] 定义的值必须为 [ViewLinkage.CUSTOM]
     *
     * #优先级
     * [LinkageAnimationHelper.coreEffector] > XML配置文件
     */
    open var coreEffector: CoreEffector? = null
        set(value) {
            if( value == null ) {
                field?.let { effectorArray.remove( it.onViewLinkage().id ) }
            }else {
                effectorArray.put( value.onViewLinkage().id, value )
            }
            field = value
        }

    private fun obtainEvent(action: Int, x: Float, y: Float, event: MotionEvent? = null): MotionEvent {
        val pollEvent = motionEventPool.removeFirstOrNull() // 尝试从对象池中获取一个事件

        // 如果没有传入 event 且池中没有空闲的 event，就创建一个新的
        if (event == null) {
            // 如果池中没有事件对象，则创建新的
            val newEvent = pollEvent?.apply {
                setAction( action )
                setLocation( x, y )
            } ?: MotionEvent.obtain(
                SystemClock.uptimeMillis(), SystemClock.uptimeMillis(), action, x, y, 0
            )
            // 将其加入到事件池中，以便未来复用
            motionEventPool.addLast( newEvent )
            return newEvent
        }
        // 如果传入了 event，进行事件的复用
        // 复用一个从池中获取的 event 或者如果池为空则创建一个新的
        val copyEvent = pollEvent ?: MotionEvent.obtainNoHistory( event )
        copyEvent.action = action
        copyEvent.setLocation( x, y )
        motionEventPool.addLast( copyEvent )
        return copyEvent
    }

    private fun recycleEvent(event: MotionEvent) {
        // 确保回收的事件对象是被正确地复用过的，且不是正在使用的事件
        if (!motionEventPool.contains(event)) {
            // 如果当前事件没有被复用过，则回收它
            event.recycle()
        } else {
            // 如果该事件仍在池中被复用，直接从池中移除
            motionEventPool.remove(event)
        }
    }

    private fun isAtTop(parent: ViewGroup): Boolean {
        return parent.scrollY <= 0
    }

    private fun isAtLeft(parent: ViewGroup): Boolean {
        return parent.scrollX <= 0
    }

    override fun getHelper(): LinkageAnimationHelper { return this }

    /**
     * 父布局继承 [ILinkage] 调用    [ILinkage.addOnTouchListener]
     */
    override fun addOnTouchListener(listener: OnTouchListener) {
        touchListeners.add( listener )
    }

    /**
     * 父布局继承 [ILinkage] 调用    [ILinkage.removeOnTouchListener]
     */
    override fun removeOnTouchListener(listener: OnTouchListener) {
        touchListeners.remove( listener )
    }

    /**
     * 父布局继承 [ILinkage] 调用    [ILinkage.addOnScrollListener]
     */
    override fun addOnScrollListener(listener: OnScrollListener) {
        scrollListener.add( listener )
    }

    /**
     * 父布局继承 [ILinkage] 调用    [ILinkage.removeOnScrollListener]
     */
    override fun removeOnScrollListener(listener: OnScrollListener) {
        scrollListener.remove( listener )
    }

    /**
     * 获取效果器
     * 优先获取缓存，如果没有则创建一个新的效果器
     * @param linkage   效果器枚举
     */
    fun getEffector(linkage: ViewLinkage?) : CoreEffector? {
        val core = coreEffector
        if( core != null ) return core
        // XML 未指定效果器
        if( linkage == null ) return null
        val k = linkage.id
        return ( if( effectorArray.containsKey( k ) ) {
            // 从缓存中获取效果器
            effectorArray.get(k)
        } else {
            // 创建默认定义的效果器
            when ( linkage ) {
                ViewLinkage.ZOOM -> ZoomEffector()
                ViewLinkage.ALPHA -> AlphaEffector()
                ViewLinkage.EXTEND -> ExtendEffector()
                ViewLinkage.EXTEND_RECOVERY -> ExtendRecoveryEffector()
                else -> null
            }
        })?.apply {
            // 更新缓存
            effectorArray.put( onViewLinkage().id, this )
        }
    }

    /**
     * 父布局调用    [ViewGroup.onFinishInflate]
     */
    fun onFinishInflate(parent: ViewGroup) {
        parent.overScrollMode = OVER_SCROLL_NEVER
    }

    open fun onTouchEvent(parent: ViewGroup, ev: MotionEvent): Boolean {
        currentAction = ev.action
        // 将触摸事件分发给所有已注册的监听器
        if ( touchListeners.isNotEmpty() ) {
            for ( it in touchListeners ) it.onTouch( parent, ev )
        }
        val currentX = ev.x
        val currentY = ev.y
        val action = ev.action
        val isAtTop = isAtTop( parent )
        val isAtLeft = isAtLeft( parent )

        // 处理 ACTION_DOWN 事件，初始化滑动的起始位置
        if ( action == MotionEvent.ACTION_DOWN ) {
            if( isAtTop ) {
                touchLastY = ev.y
                touchScrollY = parent.scrollY.toFloat()
            }
            if( isAtLeft ) {
                touchLastX = ev.x
                touchScrollX = parent.scrollX.toFloat()
            }
        }

        val distanceY = currentY - touchLastY
        touchScrollY -= distanceY

        val distanceX = currentX - touchLastX
        // 只处理向下滑动
        touchScrollX -= distanceX

        // onScroll 下发只针对 touch 小于 0 时才生效，大于 0 的下发通过 scroll 完成
        handlerOnScrollListener( action )
        touchLastY = currentY
        touchLastX = currentX
        touchOldX = touchScrollX
        touchOldY = touchScrollY
        return isInterceptTouch
    }

    /**
     * 父布局调用    [ViewGroup.onScrollChanged]
     */
    open fun onScrollChanged(parent: ViewGroup, left: Int, top: Int, oldLeft: Int, oldTop: Int) {
        if( currentAction == MotionEvent.ACTION_MOVE ||
            currentAction == MotionEvent.ACTION_HOVER_MOVE
            ) return
        touchScrollX = touch2Scroll( left )
        touchScrollY = touch2Scroll( top, false )

        handlerOnScrollListener( MotionEvent.ACTION_MOVE )
        touchOldX = touchScrollX
        touchOldY = touchScrollY
    }

    private fun handlerOnScrollListener(action: Int) {
        if( scrollListener.isEmpty() ) return
        var isIntercept = false
        val syntheticEvent = obtainEvent( action, touchScrollX, touchScrollY )
        for ( it in scrollListener ) {
            if( it.onScroll( syntheticEvent, touchOldX, touchOldY ) ) isIntercept = true
        }
        recycleEvent( syntheticEvent )
        isInterceptTouch = isIntercept
    }

    private fun touch2Scroll(
        scroll: Int, isX: Boolean = true, damping: Float = 25F
    ) : Float {
        val xy = if( isX ) touchScrollX else touchScrollY
        return xy * 1F / ( 1F + abs( scroll.toFloat() ) / ( damping * 100F ) )
    }
}